package com.tbit.uqbike.service.business.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.tbit.uqbike.convert.BadOrderMapper;
import com.tbit.uqbike.enums.BadOrderHandleType;
import com.tbit.uqbike.enums.BusinessType;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.business.UserInfo;
import com.tbit.uqbike.object.pojo.Recharge;
import com.tbit.uqbike.object.pojo.TransactionAbnormal;
import com.tbit.uqbike.object.pojo.dto.OrderRefundDTO;
import com.tbit.common.entity.view.Result;
import com.tbit.uqbike.service.business.RechargeService;
import com.tbit.uqbike.service.business.ReconciliationService;
import com.tbit.uqbike.service.business.RefundService;
import com.tbit.uqbike.webmanager.dao.capital.BadOrderDao;
import com.tbit.uqbike.webmanager.dao.capital.TransactionAbnormalDao;
import com.tbit.uqbike.webmanager.dto.AbnormalRecordDTO;
import com.tbit.uqbike.webmanager.req.AbnormalHandleReq;
import com.tbit.uqbike.webmanager.req.AbnormalRecordReq;
import com.tbit.uqbike.webmanager.resp.reconciliation.HandleDetailResp;
import com.tbit.uqbike.webmanager.util.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;

import static com.tbit.uqbike.webmanager.controller.BaseController.getLogin;

/**
 * 对账实现类
 *
 *
 * @author <a href="mailto:chen.chaoxiong@tbit.com.cn">ChenChaoXiong</a>
 * @since 2024/5/21
 */
@Service
@Slf4j
public class ReconciliationServiceImpl implements ReconciliationService {

    @Resource
    private BadOrderDao badOrderDao;

    @Resource
    private TransactionAbnormalDao transactionAbnormalDao;

    @Autowired
    private RefundService refundService;

    @Autowired
    private RechargeService rechargeService;

    private static String ORINGINAL_REFUND = "oringinalRefund";
    @Override
    public Page<AbnormalRecordDTO> list(AbnormalRecordReq req) {


        /**
         * 注意 eq(boolean condition, R column, Object val) 不是短路操作
         * 即 condition 为 false 时，也会执行 val 部分
         */
        QueryWrapper<TransactionAbnormal> queryWrapper = buildQueryWrapper(req);
        Page<TransactionAbnormal> badOrders = transactionAbnormalDao.selectPage(new Page<>(req.getPageNo(), req.getPageSize()), queryWrapper);

        List<AbnormalRecordDTO> abnormalRecordDTOS = Optional.ofNullable(badOrders.getRecords())
                .map(BadOrderMapper::poToDTO)
                .orElse(Collections.emptyList());

        return (Page<AbnormalRecordDTO>) new Page<AbnormalRecordDTO>(req.getPageNo(), req.getPageSize())
                .setRecords(abnormalRecordDTOS)
                .setTotal(badOrders.getTotal())
                .setPages(badOrders.getPages())
                .setSize(badOrders.getSize());
    }

    @Override
    public void export(AbnormalRecordReq req, HttpServletResponse response) throws IOException {
        int exportSize = 1000;
        int pageNo = 1;
        Class dataClass = AbnormalRecordDTO.class;
        String fileName = "坏账记录"+System.currentTimeMillis()+".xlsx";
        fileName = URLEncoder.encode(fileName, "UTF-8");
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), dataClass).build();
        WriteSheet writeSheet = EasyExcel.writerSheet("sheet1").build();

        QueryWrapper<TransactionAbnormal> queryWrapper = buildQueryWrapper(req);

        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);

            while (true) {
                Page<TransactionAbnormal> badOrders = transactionAbnormalDao.selectPage(new Page<>(pageNo, exportSize), queryWrapper);
                List<AbnormalRecordDTO> abnormalRecordDTOS = Optional.ofNullable(badOrders.getRecords())
                        .map(BadOrderMapper::poToDTO)
                        .orElse(Collections.emptyList());

                abnormalRecordDTOS.stream().forEach(abnormalRecordDTO -> {
                    //recordId 在excel显示时会变成科学计数法，需要在前面拼接 ` 使其显示为字符串
                   abnormalRecordDTO.setRecordId("`"+abnormalRecordDTO.getRecordId());
                });

                if (CollectionUtil.isNotEmpty(abnormalRecordDTOS)) {
                    excelWriter.write(abnormalRecordDTOS, writeSheet);
                    log.info("导出数据第 {} 批数据", pageNo);
                    pageNo++;
                } else {
                    break;
                }
            }

            excelWriter.finish();
        } catch (Exception e) {
            log.error("导出数据异常", e);
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, Object> map = new HashMap<>();
            map.put("status", "fail");
            map.put("message", "导出异常");
            response.getWriter().println(JSON.toJSONString(map));
        } finally {
//            excelWriter.close();
        }
    }

    private QueryWrapper<TransactionAbnormal> buildQueryWrapper(AbnormalRecordReq req) {

        Date beginTime = req.getBeginTime();
        Date endTime = req.getEndTime();


        QueryWrapper<TransactionAbnormal> queryWrapper = new QueryWrapper<TransactionAbnormal>();
        if (Objects.nonNull(beginTime) && Objects.nonNull(endTime)) {
            queryWrapper.and(wrapper -> wrapper
                    .between("sourceTradeTime", beginTime, endTime)
                    .or()
                    .between("targetTradeTime", beginTime, endTime));
        } else if (Objects.nonNull(req.getSourceBeginTime()) && Objects.nonNull(req.getSourceEndTime())) {
            queryWrapper.between("sourceTradeTime", req.getSourceBeginTime(), req.getSourceEndTime());
        } else if (Objects.nonNull(req.getTargetBeginTime()) && Objects.nonNull(req.getTargetEndTime())) {
            queryWrapper.between("targetTradeTime", req.getTargetBeginTime(), req.getTargetEndTime());
        }


        queryWrapper.eq(Objects.nonNull(req.getBrandId()), "brandId", req.getBrandId())
                    .in(CollectionUtil.isNotEmpty(req.getAccountIds()), "accountId", req.getAccountIds())
                    .like(Objects.nonNull(req.getRecordId()), "id", req.getRecordId())
                    .eq(StringUtils.isNotBlank(req.getChannel()), "channel", req.getChannel())
                    .eq(StringUtils.isNotBlank(req.getFlowType()), "businessType", BusinessType.getCodeByType(req.getFlowType()))
                    .eq(StringUtils.isNotBlank(req.getProcessStauts()), "handleType", BadOrderHandleType.getCodeByType(req.getProcessStauts()))
                    .eq(StringUtils.isNotBlank(req.getUnbalanceType()), "unbalanceType", req.getUnbalanceType())
                    .eq(StringUtils.isNotBlank(req.getOrdrNo()), "ordrNo", req.getOrdrNo())
                    .eq(StringUtils.isNotBlank(req.getOutOrderNo()), "outOrderNo", req.getOutOrderNo())
                    .eq(StringUtils.isNotBlank(req.getUserName()), "userName", req.getUserName())
                    .eq(StringUtils.isNotBlank(req.getUserPhone()), "userPhone", req.getUserPhone())
                    .like(StringUtils.isNotBlank(req.getOprator()), "operatorName", req.getOprator())
                    .like(StringUtils.isNotBlank(req.getOpratorPhone()), "operatorPhone", req.getOpratorPhone())
                    .orderBy(StringUtils.isBlank(req.getOrderBy()), true, Lists.newArrayList("id"));

        return queryWrapper;
    }


    @Override
    public void handleAbnormal(AbnormalHandleReq req, UserInfo userInfo) {
        TransactionAbnormal old = Optional.ofNullable(transactionAbnormalDao.selectById(req.getRecordId())).orElseThrow(() -> new BaseException("异常记录不存在"));
        String orderNo = old.getOrdrNo();
        BigDecimal refundAmount = Objects.nonNull(old.getSourceAmount()) ? old.getSourceAmount() : old.getSourceAmount();
        Recharge recharge = Optional.ofNullable(rechargeService.getRechargeByOrderNO(orderNo)).orElseThrow(() -> new BaseException("充值记录不存在"));
        if (ORINGINAL_REFUND.equals(req.getHandleType())){
            Result result = refund(recharge, userInfo, refundAmount);
            if (result.unSuccess()) {
                log.error("退款失败，退款订单号：{}，退款金额：{}", orderNo, old.getSourceAmount());
                throw new BaseException("退款失败:"+result.getMsg());
            }
        }
        TransactionAbnormal badOrder = new TransactionAbnormal();
        badOrder.setId(req.getRecordId());
        badOrder.setHandleType(BadOrderHandleType.HANDLED);
        badOrder.setOperatorId(userInfo.getAccountUserId());
        badOrder.setOperatorName(userInfo.getName());
        badOrder.setOperatorPhone(userInfo.getPhone());
        badOrder.setHandleMethod(req.getHandleType());
        badOrder.setOperatorTime(new Date());
        if (StringUtils.isNotBlank(req.getRemark())) {
            badOrder.setRemark(req.getRemark());
        }
        transactionAbnormalDao.updateById(badOrder);
    }

    /**
     * 退款
     * 走 refundService.orderRefund 方法
     * @param recharge
     * @param userInfo
     */
    private Result refund(Recharge recharge, UserInfo userInfo, BigDecimal refundAmount) {
        OrderRefundDTO orderRefundDTO = new OrderRefundDTO();
        orderRefundDTO.setOrderNO(recharge.getOrderNO());
        orderRefundDTO.setUserId(recharge.getUserId());
        orderRefundDTO.setAdAccountId(recharge.getAccountId());
        //单位 分
        orderRefundDTO.setRefundMoney(refundAmount.multiply(new BigDecimal(100)).longValue());
        return refundService.orderRefund(getLogin(ServletUtils.getRequest()), orderRefundDTO);
    }

    @Override
    public HandleDetailResp detail(Long recordId) {
        TransactionAbnormal badOrder = transactionAbnormalDao.selectById(recordId);
        return BadOrderMapper.poToHandleDetailResp(badOrder);
    }
}
